#pragma once
#include "Dispatcher.h"
#include <thread>
#include <mutex>
#include <queue>
#include <map>
#include <sys/socket.h> // socketpair
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include "Log.h"

class Dispatcher; // 因为EventLoop中含有Dispatcher，Dispatcher中含有EventLoop

enum class ElemType : char { ADD, DELETE, MODIFY }; // 处理任务队列结点的channel的方式
// 此处指定用char类型存储枚举值

typedef struct ChannelElement { // 任务队列的结点
	ElemType _type;				// 描述如何处理该结点中的channel
	Channel* _channel;          
} ChannelElement;


class EventLoop { // 反应堆: 进行事件循环
public:
	EventLoop();					    // 实例化EventLoop: 不可指定线程名
	EventLoop(const string threadName); // 实例化EventLoop: 可指定线程名
	~EventLoop();

	// _socketPair[1]读取_socketPair[0]发来的数据, 唤醒被阻塞的子线程
	static int readLocalMessage(void* arg);
	int readMessage();

	int run();							// 运行反应堆
	int eventActive(int fd, int event); // 处理被激活的fd, events为fd的就绪事件

	int addTask(Channel* channel, ElemType type); // 添加任务到任务队列: type为任务要对channel执行的操作
	int processTask(); // 处理任务队列中的任务

	// 任务队列的[增/删/改]任务的具体处理
	int add(Channel* channel);    // 增加任务: 在_channelMap中记录<fd, Channel>, 将[channel.fd]添加到[_dispathcer的fd检测集合]中
	int remove(Channel* channel); // 删除任务:
								  // 1. 将[channel.fd]从[_dispatcher的fd检测集合]中删除
								  // 2. 销毁Channel及其所属的TcpConnection，
								  // 3. 删除记录在eventLoop中的<fd, Channel>
	int modify(Channel* channel); // 修改任务: 修改[_dispatcher的fd检测集合]中channel对应fd的属性
	
	// 删除记录在_channelMap中的<fd, channel> + 关闭fd + 释放channel
	int destroyChannel(Channel* channel);

	thread::id getThreadID() { return _threadID; } // 获取所属线程的ID
private:
	void taskWakeup(); // 通过_socketPair[0]发送数据给_socketPair[1]，以激活被阻塞的子线程. 在addTask()中调用

	// 成员变量------------------------
	Dispatcher* _dispatcher; // 事件分发器: 用于指向子类的实例，完成多态。有3种子类select/poll/epoll
	bool _isQuit;            // 事件循环是否退出
	int _socketPair[2];      // 服务器本地通信的fd对, 用于激活被select/poll/epoll阻塞的子线程, 使用socketpair()初始化

	queue<ChannelElement*> _taskQueue; // 任务队列
	map<int, Channel*> _channelMap;	   // <fd, fd对应的Channel>

	// �����߳�
	thread::id _threadID; // 所属线程的ID
	string _threadName;   // 所属线程的名字
	mutex _mutex;         // 互斥锁: 保护任务队列, [1个线程对应的任务队列]可能会被[该线程]和[主线程]修改
};